package com.common.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * XML格式化工具
 *
 * @author
 */
@Slf4j
public class XmlUtils {
    private static final ConcurrentMap<Class<?>, JAXBContext> JAXB_CONTEXTS =
        new ConcurrentHashMap<Class<?>, JAXBContext>(64);

    /**
     * 对象序列化为XML字符串
     *
     * @param object
     * @return
     */
    public static String marshal(Object object) {
        try {
            Marshaller marshaller = getJaxbContext(object.getClass()).createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            /*marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);*/
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
            StringWriter writer = new StringWriter();
            marshaller.marshal(object, writer);
            return writer.toString();
        } catch (Exception e) {
            throw new RuntimeException("xml生成失败", e);
        }
    }

    /**
     * 对象序列化为XMLFile
     *
     * @param object
     * @return
     */
    public static String marshalToFile(Object object, String path) {
        StringWriter writer = new StringWriter();
        FileOutputStream fos = null;
        try {
            Marshaller marshaller = getJaxbContext(object.getClass()).createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
            fos = new FileOutputStream(path);
            marshaller.marshal(object, fos);
            marshaller.marshal(object, writer);
            return writer.toString();
        } catch (Exception e) {
            throw new RuntimeException("xml生成失败", e);
        } finally {
            try {
                writer.close();
            } catch (IOException e) {
                throw new RuntimeException("关闭StringWriter失败", e);
            }
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                throw new RuntimeException("关闭FileOutputStream失败", e);
            }
        }
    }

    /**
     * XML字符串反序列化为对象
     *
     * @param object
     * @param objectClass
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T unmarshal(String object, Class<T> objectClass) {
        try {
            return (T)getJaxbContext(objectClass).createUnmarshaller().unmarshal(new StringReader(object));
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * @param clazz
     * @return
     */
    private static final JAXBContext getJaxbContext(Class<?> clazz) {
        if (null == clazz) {
            throw new RuntimeException("clazz' must not be null");
        }
        JAXBContext context = JAXB_CONTEXTS.get(clazz);
        if (context == null) {
            try {
                context = JAXBContext.newInstance(clazz);
                JAXB_CONTEXTS.putIfAbsent(clazz, context);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
        return context;
    }

    public static String getXml(String xml) {
        String[] xmlStr = xml.split("<");
        StringBuffer upperBuffer = new StringBuffer();

        for (String str : xmlStr) {
            if (StringUtils.hasText(str)) {
                upperBuffer.append("<").append(str.substring(0, 1).toLowerCase()).append(str.substring(1));
            }
        }
        String[] upperStr = upperBuffer.toString().split("/");
        StringBuffer resultXml = new StringBuffer();
        for (int i = 0; i < upperStr.length; i++) {
            if (i != 0) {
                resultXml.append("/").append(upperStr[i].substring(0, 1).toLowerCase())
                    .append(upperStr[i].substring(1));
            } else {
                resultXml.append(upperStr[i]);
            }
        }
        return resultXml.toString();
    }
}
